﻿using System;

namespace Test30.Core
{
    [Serializable]
    public struct Vec : IEquatable<Vec>
    {
        public static readonly Vec Zero = new Vec(0, 0);
        public static readonly Vec One = new Vec(1, 1);

        #region Operators

        public static bool operator ==(Vec v1, Vec v2)
        {
            return v1.Equals(v2);
        }

        public static bool operator !=(Vec v1, Vec v2)
        {
            return !v1.Equals(v2);
        }

        public static Vec operator +(Vec v1, Vec v2)
        {
            return new Vec(v1.X + v2.X, v1.Y + v2.Y);
        }

        public static Vec operator +(Vec v1, int i2)
        {
            return new Vec(v1.X + i2, v1.Y + i2);
        }

        public static Vec operator +(int i1, Vec v2)
        {
            return new Vec(i1 + v2.X, i1 + v2.Y);
        }

        public static Vec operator -(Vec v1, Vec v2)
        {
            return new Vec(v1.X - v2.X, v1.Y - v2.Y);
        }

        public static Vec operator -(Vec v1, int i2)
        {
            return new Vec(v1.X - i2, v1.Y - i2);
        }

        public static Vec operator -(int i1, Vec v2)
        {
            return new Vec(i1 - v2.X, i1 - v2.Y);
        }

        public static Vec operator *(Vec v1, int i2)
        {
            return new Vec(v1.X * i2, v1.Y * i2);
        }

        public static Vec operator *(int i1, Vec v2)
        {
            return new Vec(i1 * v2.X, i1 * v2.Y);
        }

        public static Vec operator /(Vec v1, int i2)
        {
            return new Vec(v1.X / i2, v1.Y / i2);
        }

        #endregion

        public static bool IsDistanceWithin(Vec a, Vec b, int distance)
        {
            Vec offset = a - b;

            return offset.LengthSquared <= (distance * distance);
        }

        public Vec(int x, int y)
        {
            X = x;
            Y = y;
        }

        public int X;
        public int Y;

        public int Area => X * Y;

        public int LengthSquared => (X * X) + (Y * Y);

        public float Length => (float)Math.Sqrt(LengthSquared);

        public int RookLength => Math.Abs(X) + Math.Abs(Y);

        public int KingLength => Math.Max(Math.Abs(X), Math.Abs(Y));

        public override string ToString()
        {
            return X.ToString() + ", " + Y.ToString();
        }


        public override bool Equals(object obj)
        {
            if (obj is Vec)
            {
                return Equals((Vec)obj);
            }
            else
            {
                return false;
            }
        }

        public override int GetHashCode()
        {
            return ToString().GetHashCode();
        }

        public bool Contains(Vec vec)
        {
            if (vec.X < 0) return false;
            if (vec.X >= X) return false;
            if (vec.Y < 0) return false;
            if (vec.Y >= Y) return false;

            return true;
        }

        public bool IsAdjacentTo(Vec other)
        {
            // not adjacent to the exact same position
            if (this == other) return false;

            Vec offset = this - other;

            return (Math.Abs(offset.X) <= 1) && (Math.Abs(offset.Y) <= 1);
        }

        public Vec Offset(int x, int y)
        {
            return new Vec(X + x, Y + y);
        }

        public Vec OffsetX(int offset)
        {
            return new Vec(X + offset, Y);
        }

        public Vec OffsetY(int offset)
        {
            return new Vec(X, Y + offset);
        }

        public Vec Each(Func<int, int> function)
        {
            if (function == null) throw new ArgumentNullException("function");

            return new Vec(function(X), function(Y));
        }

        

        #region IEquatable<Vec> Members

        public bool Equals(Vec other)
        {
            return X.Equals(other.X) && Y.Equals(other.Y);
        }

        #endregion
    }
}
